        TITL DAI FIRMWARE D3F4-D61F  V1.1
        ORG   :D3F4
*
*
*
********************
* READ FILE HEADER *
********************
*
* Locates a file on tape and reads leader.
* Exit: Interrupts are disabled. BCDEHL preserved.
*
RHDR    CALL  :D98F     Disable sound interrupt
        CALL  :D480     Find sync pattern
        CALL  :D4D4     Read flag type byte
        JC    :D3F4     Again if reading error
        CPI   :55
        JNZ   :D3F4     Again if not flag byte
        CALL  :D4D4     Read file type byte
        JC    :D3F4     Again if reading error
        RET
*
*********************
* WRITE FILE LEADER *
*********************
*
* Writes a leader for program or data block on tape.
* Disables interrupts which could cause problems.
*
* Entry: at WHDR:  Entry if not during run program.
*        at WHD20: If during run of program.
* Exit:  BCDEHL preserved.
*
WHDR    CALL  :DAFF     Print 'SET RECORD, START
        DBL   :DB9C     TAPE, TYPE SPACE'
        CALL  :D7CB     Wait for spacebar pressed
WHD20   CALL  :D42E     Switch on cassette motors
        DI              Disable interrupts
        NOP
        NOP
        CALL  :D4ED     Write leader
        MVI   A,:55     Get flag byte
        JMP   :D509     Write flag byte
*
**************
* (Not used) *
**************
*
MPT27   CALL  :D2F1     Write block on tape
        NOP
        NOP
*
**********************
* WRITE FILE TRAILER *
**********************
*
* Write a trailer for program or datablock.
*
* Entry: Length of trailer in C.
* Exit:  A=0, BCDEHL preserved.
*
*WTRL
CWCLOS  CALL  :D550     Write trailer bytes
        EI              Enable interrupts
        JMP   :D445     Stop cassette motors
*
*************************
* START CASSETTE MOTORS *
*************************
*
* Turns on motor of selected cassettedeck and
* waits 665 msec.
*
* Exit: All registers preserved.
*
CASST   PUSH  PSW
        LDA   :0040     Load POROM
        ORI   :30       Disable cassette motors
        PUSH  H
        LXI   H,:013D   Addr CASSL
        XRA   M         Get selected cassette
        POP   H
        STA   :0040     Remember POROM
        STA   :FD06     Switch cassette motor on
        CALL  :DE41     Delay
        POP   PSW
        RET
*
************************
* STOP CASSETTE MOTORS *
************************
*
* Switches off cassettemotors.
*
* Exit: All registers preserved.
*
*CRCLOS
CASSP   PUSH  PSW
        LDA   :0040     Load POROM
        ORI   :30       Disable cassette motors
        STA   :0040     Remember POROM
        STA   :FD06     Switch cassette motors off
        POP   PSW
        RET
*
************
* READ BIT *
************
*
* Reads one bit from tape.
*
* Entry: Address input port in HL. Input low state.
* Exit:  CY=0: sign bit of A is bit read.
*        CY=1: reading error.
*        BHL preserved.
*
RBIT    XRA   A
        MOV   D,A
        MOV   B,A
        MOV   C,A
 REM
* 1st impulse:
 REM
RBT10   DCR   B
        JZ    :D47E     Too long low
        ORA   M
        JP    :D457     Wait for high
RBT30   DCR   C
        JZ    :D47E     Too long high
        DCR   D
        ANA   M
        JM    :D45F     Wait low
        LXI   B,:0000
 REM
* 2nd impulse:
 REM
RBT40   DCR   B
        JZ    :D47E     Too long low
        ORA   M
        JP    :D46B     Wait high again
RBT50   DCR   C
        JZ    :D47E     Too long high
        INR   D
        ANA   M
        JM    :D473     Wait low
        MOV   A,D
        RET
 REM
* If error:
 REM
RBT90   STC             Set CY if error
        RET
*
***************
* READ LEADER *
***************
*
* Finds a section of leader on the tape.
*
* Entry: No conditions.
* Exit:  BCDEHL preserved, interrupts disabled.
*
RLEAD   PUSH  B
        PUSH  D
        PUSH  H
        MVI   B,:28     Estimate of impulse length
        LXI   H,:FD00   Address input port
RDL05   MVI   A,:FF
RDL10   EI              Enable interrupts
        NOP             (here cursor flashes)
        DI              Disable interrupts
        ANA   M
        JM    :D48A     Wait low
        MOV   C,B       Estimated length in C
        MVI   D,:14     Needs this many cycles for
 REM synchronisation. Must be
 REM more than trailer length.
RDL30   MVI   E,:00
        XRA   A
RDL40   DCR   E
        JZ    :D488     Too long low; start again
        ORA   M
        JP    :D497     Wait high
        MVI   B,:00
RDL50   INR   B
        JZ    :D488     Too long high; start again
        ANA   M
        JM    :D4A1     Wait low
        MOV   A,B
        SUB   C         Compare impulse length
 REM with estimate
        JP    :D4B0
        CMA             )
        INR   A         ) 2-complement if <0
RDL60   MOV   E,A       Store difference in E
        MOV   A,C       Calculate margin
        ANI   :F0
        RAR
        RAR
        RAR             Margin: 1/8th of estimate
        CMP   E         Compare with difference
        JC    :D4C3     Not within margin
 REM
* If sync archieved:
 REM
        DCR   D
        JNZ   :D494     Next impulse until D=0
        INR   D
        JMP   :D494     Next impulse until out
 REM of margin
 REM
* If out of margin:
 REM
RDL70   DCR   D
        JNZ   :D488     Not synchronised; again
        XRA   A
RDL80   ORA   M
        JP    :D4C8     Wait high
RDL90   ANA   M
        JM    :D4CC     Wait low
        POP   H
        POP   D
        POP   B
        RET
*
*************
* READ BYTE *
*************
*
* Reads one byte from tape.
*
* Entry: No conditions.
* Exit:  CY=0: Byte read in A.
*        CY=1: Some error.
*        BCDEHL preserved.
*
RBYTE   PUSH  B
        PUSH  D
        PUSH  H
        LXI   H,:FD00   Address input port
        MVI   E,:FE
RBY10   CALL  :D453     Read bit
        JC    :D4E9     If reading error; CY=1
        RAL
        MOV   A,E
        RAL
        MOV   E,A       Shift bit into E
        JC    :D4DC     Next bit
RBY20   POP   H         8 bits read, no error
        POP   D
        POP   B
        RET
*
****************
* WRITE LEADER *
****************
*
* Writes a leader on the tape. From WLD10 also used
* to write a trailer.
*
* Entry: No conditions.
* Exit:  A=0, BCDEHL preserved.
*
WLEAD   NOP
        PUSH  B
        PUSH  H
        LHLD  :02E6     Get leader impulse length
        LXI   B,:07E8   Period for synchr.
WLD10   CALL  :D524     Write bit
        DCX   B
        MOV   A,B
        ORA   C
        JNZ   :D4F6     Write many bits
        LHLD  :02E8     Get impulse length data bit
        CALL  :D524     Write a data '1' bit to end
        POP   H
        POP   B
        NOP
        RET
*
**************
* WRITE BYTE *
**************
*
* Write a byte to tape.
*
* Entry: Byte to be written in A.
* Exit:  All registers preserved.
*
WBYTE   PUSH  PSW
        PUSH  B
        PUSH  D
        PUSH  H
        LHLD  :02E8     Get impulse length bit '1'
        MOV   E,H
        MOV   D,L       DE: inpulse length bit '0'
        MVI   B,:08     8 bits to write
WBY10   RAL             Set/reset CY for kind of bit
        CC    :D524     Write data '1' bit
        XCHG
        CNC   :D524     Write data '0' bit
        XCHG
        DCR   B
        JNZ   :D514     Next bit
        JMP   :CB56     Pop all, ret
*
*************
* WRITE BIT *
*************
*
* Write 2 impulses on tape, one long, one short.
*
* Entry: H: Half count first cycle.
*        L: Half count second cycle.
* Exit:  All registers preserved.
*
WBIT    PUSH  PSW
        PUSH  D
        PUSH  H
        MOV   L,H
        LXI   D,:FD06   Address output port
        CALL  :D53C     Write 1st impulse
        POP   H
        PUSH  H
        MOV   H,L
        MOV   A,L
        SUI   :08       Allow for return to WBYTE
        MOV   L,A
        CALL  :D53C     Write 2nd impulse
        POP   H
        POP   D
        POP   PSW
        RET
*
***************
* WRITE CYCLE *
***************
*
* Writes one impulse (hi/lo) on tape. Two cycles
* are required for one bit.
*
* Entry: DE: Address output port.
*        HL: Impulse length constants.
* Exit:  HL = 0. BCDE preserved.
*
WCYC    LDA   :0040     POROM in A
        ORI   :01       lsb = 1
        STAX  D         Output port is made '1'
WCY10   DCR   H
        JNZ   :D542     Write '1' until H=0
        DCR   L
        DCR   L
        DCR   L         Allow for return to WBIT
        DCR   A
        STAX  D         Output port is made '0'
WCY20   DCR   L
        JNZ   :D54B     Write '0' until L=0
        RET
*
**********************
* WRITE TRAILER BITS *
**********************
*
* Writes trailer bits after a block on tape.
*
* Entry: Number of trailer bits in C.
* Exit:  A=0, other registers preserved.
*        F corrupted.
*
WTRLX   PUSH  B
        PUSH  H
        LHLD  :02EA     Trailer impulse length
        MVI   B,:00
        JMP   :D4F6     Write trailer bits
        STA   :0131     ] Output to screen
        JMP   :DD49     ] Ignore line if break
 REM pressed
*
********************************
* INITIALISE KEYBOARD POINTERS *
********************************
*
* Set all keyboard pointers to default values.
*
* Entry: Address ASCII-table in HL (3E8C5).
* Exit:  BCDE preserved.
*
*KLIRS
KBINIT  SHLD  :02A7     Load pointer ASCII-table
KLIRP   XRA   A
        STA   :02B9     Allow complete scan routine
        STA   :02C3     CTRL not pressed
        LXI   H,:02BA
        SHLD  :02BE     Set KLIIN ) Ignore
        SHLD  :02C0     Set KLIOU ) previous inputs
        CMA
        STA   :02C4     BREAK pointer = FF
        RET
*
**************************************
* KEYBOARD INTERRUPT SERVICE (RST 6) *
**************************************
*
* Current interrupt mask is saved. Only stack and
* clock interrupts are allowed. Keyboard timer 4
* is re-loaded.
* KBXCT is counted down: abort if not 0.
* Else: scan keyboard and store result.
*
* Entry: None.
* Exit:  All registers + int. mask preserved.
*
KBINT   PUSH  PSW
        PUSH  B
        PUSH  D
        LDA   :005F
        PUSH  PSW       Preserve current int. mask
        MVI   A,:84     )
        STA   :FFF8     ) Allow stack and clock
        STA   :005F     ) interrupts only
        EI
        CALL  :D99D     Reload keyboard timer
        LXI   H,:01C1
        DCR   M         Decr. keyb.scan time count
        JNZ   :D9CD     No scanning if <>0
 REM
* if KBXCT = 0:
 REM
        MVI   M,:02     Set keyb. scan time counter
        CALL  :D59A     Scan keyboard, store result
        JMP   :D9CD     Restore int.mask; ret.
*
*******************************
* SCAN KEYBOARD, STORE RESULT *
*******************************
*
* Exit: All registers corrupted.
*
KBSCAN  LXI   D,:FFF1   Input port from keyboard
        LXI   H,:FFF7   Output port to keyboard
        LXI   B,:02C4   BREAK pointer
        DI
        MVI   M,:40     Scan row 6
        LDAX  D         and get result
        EI
        ADD   A         Check for BREAK pressed
        JM    :D606     If BREAK pressed
        CALL  :D750     Update BREAK pointer
        LDA   :02B9     Get BREAK pointer
        ORA   A         Scan for BREAK only?
        JNZ   :D605     Then abort
 REM
* Scan all rows and store result in MAP1:
 REM
        LXI   B,:02A9   MAP1 for currently
 REM pressed key
        PUSH  B         Preserve MAP1 addr
        INR   A         Determine row
KEB10   PUSH  PSW
        DI
        MOV   M,A       Scan row
        LDAX  D         Get result
        EI
        STAX  B         Store result in MAP1
        INX   B
        POP   PSW
        ADD   A         Determine next row
        JNC   :D5BB     Scan next row if not ready
 REM
* REPT handling:
 REM
        LDA   :02AF
        ANI   :20       Check if REPT pressed
        MOV   B,A       Store result
        LXI   H,:02C2   Addr. REPT counter
        MOV   A,M       Get contents
        MVI   M,:01     Update it for immediate scan
        JZ    :D5DF     If REPT not pressed
        DCR   A         Else
        MOV   M,A       Decr. REPT counter
        JNZ   :D604     If <>0, abort scan
        MVI   M,:02     Else RPCNT=2
        MVI   B,:FF     Set B=FF for REPT pressed
 REM
* ASCII-value of key pressed into KLIND:
 REM
KEB40   POP   H         Get addr MAP1
        PUSH  H         Save addr. MAP1
        LXI   D,:02B1   Get addr. MAP2
        MVI   C,:00
KEB50   MOV   A,M       Get result scan current
 REM row in A
        DCR   B
        INR   B         REPT pressed ?
        JNZ   :D5F0     If REPT pressed
        XCHG
        XRA   M         ) Check if new input
        XCHG            )
        ANA   M         )
KEB60   ORA   A         )
        CNZ   :D632     If new: Get ASCII-code
 REM and store it in KLIND
        INX   D
        INX   H         Next row
        INR   C
        MOV   A,C
        CPI   :08       All rows checked?
        JNZ   :D5E6     Next row if not
KEB70   POP   D         Get MAP1 addr in DE
        PUSH  D
        MOV   B,H       ) Get MAP2 addr in HL
        MOV   C,L       )
        CALL  :DE4F     Transfer (MAP1) into MAP2
KEB80   POP   D         Scrap
KEB81   RET
 REM
* if BREAK pressed:
 REM
KEB90   PUSH  B         Save Breakpntr
        CALL  :D8A6     All sound off
        POP   B
        NOP
        CALL  :D445     Stop cassette motors
        LDAX  B         Get KBRFL
        INR   A
        JZ    :D605     If KBRFL=FF: break acknow-
 REM ledged already
        STAX  B         Else: store new KBRFL
        CPI   :20
        JNZ   :D605     If new KBRFL<>20: wait for
 REM soft-break to be accepted
        CALL  :D563     Else: init keyb. pointers
        JMP   :C80C     Print 'BREAK', return to
 REM monitor
*
*
        UNL
KLIRS   EQU KBINIT
CRCLOS  EQU CASSP
WTRL    EQU CWCLOS
        LST
        END
